package com.xyy.saas.payment.adpater.impl;

import com.alibaba.fastjson.JSON;
import com.unionpay.acp.sdk.SDKConfig;
import com.unionpay.acp.sdk.SDKUtil;
import com.unionpay.acp.sdksample.multicert.DemoBase;
import com.xyy.framework.redis.core.RedisClient;
import com.xyy.saas.payment.adpater.PaymentAdapter;
import com.xyy.saas.payment.adpater.channel.setting.ChannelSettingManager;
import com.xyy.saas.payment.adpater.unionpay.UnionPayChannelHandler;
import com.xyy.saas.payment.common.exception.PaymentException;
import com.xyy.saas.payment.cores.bo.PayBo;
import com.xyy.saas.payment.cores.bo.PrepayBo;
import com.xyy.saas.payment.cores.bo.QueryResultBo;
import com.xyy.saas.payment.cores.enums.ResultCodeEnum;
import com.xyy.saas.payment.cores.vo.UnionPayConfigVo;
import com.xyy.saas.payment.dao.model.PrePayOrder;
import com.xyy.saas.payment.payment.core.bo.RefundBO;
import com.xyy.saas.payment.payment.core.dto.PayDto;
import com.xyy.saas.payment.payment.core.dto.PrepayDto;
import com.xyy.saas.payment.payment.core.dto.RefundDto;
import com.xyy.saas.payment.payment.core.service.PrePayOrderService;
import com.xyy.saas.payment.util.DateUtil;
import com.xyy.saas.payment.util.UnionPayUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;

/**
 * @Author zhangwenxiong
 * @Date 2020/11/24 15:47
 * @Version 1.0
 */
@Service
public class UnionPayAdapter implements PaymentAdapter, InitializingBean {
    private static final Logger LOGGER = LoggerFactory.getLogger(UnionPayAdapter.class);

    public static Map<String, Object> map = new HashMap<>();

    @Autowired
    private PrePayOrderService prePayOrderService;

    @Autowired
    private ChannelSettingManager channelSettingManager;

    @Autowired
    private ApplicationContext applicationContext;
    @Autowired
    private UnionPayUtils unionPayUtils;

    private Map<String,UnionPayChannelHandler> unionPayChannelHandlerMap = new HashMap<>();

    @Override
    public PrepayBo prepay(PrepayDto prepayDto) throws PaymentException {
        return prePayOrderService.prepayOrderByPrepayDto(prepayDto);
    }

    //推荐调用支付接口直接调用这个方法
    @Override
    public PayBo pay(PayDto payDto) throws PaymentException {
        PayBo payBo = unionPayChannelHandlerMap.get(payDto.getPayMode()).pay(payDto);
        return payBo;
    }

    @Override
    public RefundBO refund(RefundDto refundDto) throws PaymentException {

        RefundBO refundBO = unionPayChannelHandlerMap.get(refundDto.getPayMode()).refund(refundDto, refundDto.getReceiverId());
        return refundBO;
    }

    @Override
    public QueryResultBo queryPayStatus(String tradeNo, PrePayOrder prePayOrder, String payMode) throws PaymentException {

        QueryResultBo bo = new QueryResultBo();
        try{
            UnionPayConfigVo unionPayConfigVo = channelSettingManager.getUnionPayConfig(prePayOrder.getBusinessOrderType(), payMode);

            if (unionPayConfigVo == null) {
                throw new PaymentException(ResultCodeEnum.UNIONPAY_CONFIG_NOT_EXITS);
            }

            unionPayUtils.init(unionPayConfigVo);
            SortedMap paramsMap = unionPayUtils.joinQueryRequestData(tradeNo, prePayOrder.getCreateTime());

            //生成签名
            Map<String, String> signMap = unionPayUtils.signData(paramsMap);
            LOGGER.info("UnionPayAdapter#queryPayStatus invoke remote pay_no:{}, params:{}",prePayOrder.getPayNo(), JSON.toJSONString(signMap));

            //发送请求
            Map<String, String> resultMap = unionPayUtils.post(SDKConfig.getConfig().getSingleQueryUrl(),signMap);
            LOGGER.info("UnionPayAdapter#queryPayStatus invoke remote success pay_no:{},result:{}", prePayOrder.getPayNo(), JSON.toJSONString(resultMap));
            //处理返回结果
            if (!SDKUtil.validate(resultMap, DemoBase.encoding)){
                throw new PaymentException(ResultCodeEnum.SIGN_ERROR);
            }

            if("00".equals(resultMap.get("respCode"))&&"00".equals(resultMap.get("origRespCode"))){
                //拼接业务处理信息
                String orderId = resultMap.get("orderId");
                String queryId = resultMap.get("queryId");
                String txnAmt = resultMap.get("txnAmt");
                Long payTime = StringUtils.isNotEmpty(resultMap.get("txnTime"))? DateUtil.parase(resultMap.get("txnTime"), DateUtil.DATEFORMATDAY_02).getTime() : DateUtil.parase(DateUtil.format(new Date(), DateUtil.DATEFORMATDAY_02), DateUtil.DATEFORMATDAY_02).getTime();

                bo.setChannelChargeNo(queryId);
                bo.setAmount(Integer.parseInt(txnAmt));
                bo.setSuccessTime(payTime);
                bo.setStatus(QueryResultBo.STATUS_FINISHED);
                LOGGER.info("UnionPayAdapter#queryPayStatus create success pay_no:{},result:{}", prePayOrder.getPayNo(), JSON.toJSONString(bo));
                return bo;

            }else {
                bo.setStatus(QueryResultBo.STATUS_PROCESSING);
                LOGGER.info("UnionPayAdapter#queryPayStatus create fail pay_no:{},result:{}", prePayOrder.getPayNo(), JSON.toJSONString(bo));
                return bo;
            }

        }catch(PaymentException e){
            LOGGER.info("UnionPayAdapter#queryPayStatus create success pay_no:{},result:{}", prePayOrder.getPayNo(), JSON.toJSONString(bo), e);
            throw e;

        }catch (Exception e){
            LOGGER.info("UnionPayAdapter#queryPayStatus create success pay_no:{},result:{}", prePayOrder.getPayNo(), JSON.toJSONString(bo), e);
            throw new PaymentException(ResultCodeEnum.DEFAULT_ERROR);
        }

    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Map<String, UnionPayChannelHandler> unionPayChannel = applicationContext.getBeansOfType(UnionPayChannelHandler.class);
        for (UnionPayChannelHandler item: unionPayChannel.values()) {
            unionPayChannelHandlerMap.put(item.getMethod(), item);
        }
    }
}
